package com.bytz.modules.cms.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.bytz.common.exception.BytzBootException;
import com.bytz.modules.cms.domain.model.pay.BalancePayIn;
import com.bytz.modules.cms.domain.model.pay.PayInBase;
import com.bytz.modules.cms.domain.model.pay.PayOut;
import com.bytz.modules.cms.domain.model.pay.PrepayIn;
import com.bytz.modules.cms.order.constant.OrderPayConstants;
import com.bytz.modules.cms.order.entity.Order;
import com.bytz.modules.cms.order.entity.OrderItem;
import com.bytz.modules.cms.order.entity.OrderPay;
import com.bytz.modules.cms.order.entity.OrderPayDetail;
import com.bytz.modules.cms.order.model.GetOrderPayListOut;
import com.bytz.modules.cms.order.model.OrderPayDetailModel;
import com.bytz.modules.cms.order.model.OrderPayModel;
import com.bytz.modules.cms.order.service.*;
import com.bytz.modules.cms.reseller.entity.Reseller;
import com.bytz.modules.cms.reseller.service.IResellerService;
import com.google.common.collect.Range;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static com.bytz.modules.cms.order.constant.OrderConstants.OrderStatus.CAN_BALANCE_PAY_STATUS;
import static com.bytz.modules.cms.order.constant.OrderConstants.OrderStatus.CAN_PREPAY_STATUS;

@Service
public class MallOrderPayServiceImpl implements IMallOrderPayService {
    @Autowired
    private IOrderPayService orderPayService;

    @Autowired
    private IResellerService resellerService;

    @Autowired
    private IOrderService orderService;

    @Autowired
    private IOrderItemService orderItemService;

    @Autowired
    private IOrderPayDetailService orderPayDetailService;

    @Override
    public GetOrderPayListOut getByOrderId(String orderId) {
        List<OrderPay> orderPays = orderPayService.getByOrderId(orderId);
        List<OrderPayDetail> orderPayDetails = orderPayDetailService.getByOrderId(orderId);

        Map<String, List<OrderPayDetail>> payDetailMap = orderPayDetails.stream()
                .collect(Collectors.groupingBy(OrderPayDetail::getOrderPayId));

        List<OrderPayModel> orderPayResults = orderPays.stream().map(orderPay -> {
            OrderPayModel orderPayModel = BeanUtil.toBean(orderPay, OrderPayModel.class);
            orderPayModel.setPayDetails(Optional.of(payDetailMap.get(orderPay.getId()))
                    .map(orderPayDetail -> {
                        List<OrderPayDetailModel> detailModels = BeanUtil.copyToList(orderPayDetail, OrderPayDetailModel.class);

                        // 计算额外属性
                        return detailModels.stream().peek(detailModel -> {
                            boolean onlinePay = !OrderPayConstants.PayWay.OFFLINE.equals(detailModel.getPayWay());
                            boolean paying = OrderPayConstants.PayStatus.PAYING.equals(detailModel.getStatus());
                            Boolean checkTranDate = checkTranDate(detailModel.getTranDate());
                            boolean expirationTimeIsNull = detailModel.getExpirationTime() == null;

                            // 线上支付 && 支付中 && 支付过期时间不为空
                            detailModel.setCanContinuePay(onlinePay && paying && !expirationTimeIsNull);
                            // 线上支付 && 支付中 && 支付过期时间为空
                            detailModel.setCanQueryPay(onlinePay && paying && expirationTimeIsNull);
                        }).sorted(Comparator.comparing(OrderPayDetailModel::getCreateTime)).collect(Collectors.toList());
                    }).orElse(Collections.emptyList()));

            return orderPayModel;
        }).sorted(Comparator.comparing(OrderPayModel::getCreateTime)).collect(Collectors.toList());

        Order order = orderService.getById(orderId);

        GetOrderPayListOut result = new GetOrderPayListOut();
        result.setOrderPays(orderPayResults);
        result.setCanStartPrepay(canStartPrepay(order, orderPayResults));
        result.setCanStartBalancePay(canStartBalancePay(order, orderPayResults));

        return result;
    }
    /**
     * 判断当前日期是否在tranDateStr的正负一天的范围
     * @param tranDateStr tranDateStr
     * @return 是否在范围(true 在，false 不在) true的话证明该tranDate可用 false证明tranDate不在正负一天的范围则不可用
     */
    private Boolean checkTranDate(String tranDateStr){
        if (StringUtils.isBlank(tranDateStr)) {
            return false;
        }

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        LocalDate targetDate = LocalDate.parse(tranDateStr, formatter);
        Range<LocalDate> range = Range.closed(targetDate.minusDays(1), targetDate.plusDays(1));
        return range.contains(LocalDate.now());
    }

    @Override
    public BigDecimal calculatePrepayPrice(String orderId) {
        Order order = orderService.getById(orderId);
        List<OrderItem> orderItems = orderItemService.getByOrderId(orderId);

        return orderPayService.calculatePrepayPrice(order, orderItems);
    }

    @Override
    public PayOut prepay(PrepayIn prepay) {
        checkPaymentPassword(prepay);
        return orderPayService.prepay(prepay);
    }

    @Override
    public PayOut balancePay(BalancePayIn balance) {
        checkPaymentPassword(balance);
        return orderPayService.balancePay(balance);
    }

    /**
     * 检查支付口令是否正确
     */
    private void checkPaymentPassword(PayInBase payIn) throws BytzBootException {
        Order order = orderService.getById(payIn.getOrderId());
        Reseller reseller = resellerService.getById(order.getResellerId());
        if (StringUtils.isBlank(reseller.getPaymentPassword())) {
            return;
        }
        if (!payIn.getPaymentPassword().equals(reseller.getPaymentPassword())) {
            throw new BytzBootException("支付口令错误！");
        }
    }

    /**
     * 是否能发起首付支付
     */
    private boolean canStartPrepay(Order order, List<OrderPayModel> orderPayResults) {
        // 状态允许，没有支付中的支付记录
        return CAN_PREPAY_STATUS.contains(order.getStatus()) &&
                orderPayResults.stream()
                        .noneMatch(p -> OrderPayConstants.PayStatus.PAYING.equals(p.getStatus()));
    }

    /**
     * 是否能发起尾款支付
     */
    private Boolean canStartBalancePay(Order order, List<OrderPayModel> orderPayResults) {
        // 状态允许，且没有支付中的支付记录
        return CAN_BALANCE_PAY_STATUS.contains(order.getStatus()) &&
                orderPayResults.stream()
                        .noneMatch(p -> OrderPayConstants.PayStatus.PAYING.equals(p.getStatus()));
    }
}